<?php
/**
 *  A tree element is a wrapper for a value, and as such it forwards
 *      ArrayAccess method calls to the value
 *  NOTE: Iterator methods iterate over the element's children
 *
 *
 */
class Yz_Tree_Element
    extends Yz_Class
    implements ArrayAccess, Iterator, Yz_Tree_Element_Interface
{

    protected $children = array();
    protected $id;
    protected $value;
    protected $parentElement;
    protected $tree;

    /** Element constructor
     *  (Called by Tree::createElement())
     *  Do not test the id here because it should have been verified by the Tree::createElement()
     *  If nonetheless the Tree::createElement() was bypassed, testing the id is useless,
     *  as the element does not really belong to the tree.
     *  @param Yz_Tree_Interface
     *  @param int
     *  @param object
     *  @throw Exception if value is not appropriate
     */
    public function __construct(Yz_Tree_Interface $tree, $id, $value)
    {
        debug();
        $this->id = $id;
        $this->assignValue($value);
        $this->tree = $tree;
    }

    /** add an element as a child of this element
     *  Check integrity by verifying that both elements belong to the same tree
     *  @todo notify the tree that the element's parent has changed
     *  @param Yz_Tree_Element_Interface
     *  @return Yz_Tree_Element_Interface
     */
    public function addChild(Yz_Tree_Element_Interface $el)
    {
        if (! $this->tree->inTree($el)) {
            throw new Yz_Exception("Elements do not belong to the same tree");
        }
        $el->parentElement = $this;
        $this->children[$el->id] = $el;
        return $el;
    }

    public function removeChild(Yz_Tree_Element_Interface $el)
    {
        debug();
        if ($el->parentElement !== $this) {
            throw new Yz_Exception("Not a child of this element");
        }
        $el->parentElement = null;
        unset($this->children[$el->id]);
        return $el;
    }

    public function depth()
    {
        debug();
        if (null === $this->parentElement) {
            return 0;
        }
        return $this->parentElement->depth() + 1;
    }

    public function children()
    {
        return new Yz_Array($this->children);
    }

    public function hasChildren()
    {
        return count($this->children) > 0;
    }

    public function id()
    {
        return $this->id;
    }

    public function value()
    {
        return $this->value;
    }

    public function parentElement()
    {
        debug();
        return $this->parentElement;
    }

    public function path()
    {
        debug();
        $pe = $this->parentElement;
        if (null === $pe) {
            return new Yz_Array(array($pe));
        }
        return $pe->path()->push($this);
    }

    public function assignValue($value)
    {
        $this->value = $this->adjustValue($value);
        return $this;
    }


    /**
    *   @return bool
    */
    public function offsetExists( $index )
    {
        return $this->value->offsetExists($index);
    }

    /**
    *   @return mixed
    */
    public function offsetGet( $index )
    {
        return $this->value->offsetGet($index);
    }

    /**
    *   @return chainable
    */
    public function offsetSet( $index, $value )
    {
        $this->value->offsetSet($index, $value);
        return $this;
    }

    /**
    *   @return chainable
    */
    public function offsetUnset( $index )
    {
        $this->value->offsetUnset($index);
        return $this;
    }



    /********************************
     *      Iterator methods        *
     ********************************/

    /**
     * @return Yz_Tree_Element_Interface|null
     */
    public function current()
    {
        return current($this->children);
    } //

    /**
     * @return chainable
     */
    public function next()
    {
        next($this->children);
        return $this;
    } //

    /**
     * @return int|null
     */
    public function key()
    {
        return key($this->children);
    } //

    /**
     * @return bool
     */
    public function valid()
    {
        return null !== key($this->children);
    } //

    /**
     * @return chainable
     */
    public function rewind()
    {
        rewind($this->children);
        return $this;
    } //


    protected function adjustValue($value)
    {
        if ($value instanceof Yz_Class) {
        } else if (null === $value || is_scalar($value)) {
            $value = new Yz_Hash(array(
                'id' => $this->id,
                'value' => $value,
                ));
        } else {
            $value = new Yz_Hash($value);
        }
        return $value;
    }


}
